package org.xydpeng.framework.l2cache.configuration;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import org.springframework.boot.autoconfigure.cache.CacheProperties;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Primary;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.listener.RedisMessageListenerContainer;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import org.xydpeng.framework.l2cache.message.RedisCacheMessageMulticaster;
import org.xydpeng.framework.l2cache.properties.L2CacheConfigProperties;
import org.xydpeng.framework.l2cache.support.RedisCaffeineL2Cache;
import org.xydpeng.framework.l2cache.support.RedisCaffeineL2CacheManager;
import org.xydpeng.framework.l2cache.support.RedisSerializerFactory;

/**
 * @author xydpeng
 * @version v1.0
 * @date: 2024/6/11
 * @description:
 */
@AutoConfiguration(after = RedisAutoConfiguration.class)
@EnableConfigurationProperties(L2CacheConfigProperties.class)
@ConditionalOnProperty(name = "spring.cache.l2cache.enable", havingValue = "true", matchIfMissing = false)
public class TwolevelsCacheAutoConfiguration {

    @Bean
    @ConditionalOnMissingBean(name = "stringKeyRedisTemplate")
    public RedisTemplate<String, Object> stringKeyRedisTemplate(L2CacheConfigProperties properties, RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(redisConnectionFactory);
        template.setKeySerializer(new StringRedisSerializer());
        template.setHashKeySerializer(new StringRedisSerializer());
        RedisSerializer redisSerializer = RedisSerializerFactory.createInstance(properties.getSerialization());
        template.setValueSerializer(redisSerializer);
        template.setHashValueSerializer(redisSerializer);
        return template;
    }

    @Bean
    @ConditionalOnMissingBean
    @ConditionalOnBean(RedisTemplate.class)
    @Primary
    public RedisCaffeineL2CacheManager redisCaffeineL2CacheManager(L2CacheConfigProperties properties,
                                                    @Autowired(required = false) CacheProperties cacheProperties,
                                                    @Qualifier("stringKeyRedisTemplate") RedisTemplate<Object, Object> stringKeyRedisTemplate) {

        RedisCaffeineL2CacheManager cacheManager = new RedisCaffeineL2CacheManager(properties, cacheProperties,
                stringKeyRedisTemplate);
        return cacheManager;
    }


    @Bean
    @ConditionalOnMissingBean(name = "cacheMessageListenerContainer")
    public RedisMessageListenerContainer cacheMessageListenerContainer(RedisConnectionFactory redisConnectionFactory) {
        RedisMessageListenerContainer container = new RedisMessageListenerContainer();
        container.setConnectionFactory(redisConnectionFactory);
        return container;
    }

    @Bean
    public RedisCacheMessageMulticaster CacheMessageMulticaster(L2CacheConfigProperties properties,
                                                                @Qualifier("stringKeyRedisTemplate") RedisTemplate<Object, Object> stringKeyRedisTemplate,
                                                                RedisMessageListenerContainer container,
                                                                @Qualifier("redisCaffeineL2CacheManager") RedisCaffeineL2CacheManager cacheManager) {
        RedisCacheMessageMulticaster messageMulticaster = new RedisCacheMessageMulticaster(properties, stringKeyRedisTemplate, container);
        messageMulticaster.addMessageListener(message -> {
            if (properties.getInstanceId() != message.getInstanceId()) {
                return;
            }
            RedisCaffeineL2Cache cache = (RedisCaffeineL2Cache) cacheManager.getCache(message.getCacheName());
            cache.invalidate(message.getKey());
        });
        return messageMulticaster;
    }


}
